// =============================================================================
//  STATS - Statistical Analysis Tools, v.0.01
//  Copyright (C) 2011  Marco Vettigli
// -----------------------------------------------------------------------------
//
// This program  is free software:  you can  redistribute it and/or  modify it
// under the terms of the  GNU General Public License as published by the Free
// Software Foundation,  either version 3 of the License,  or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY;  without even  the  implied  warranty  of  MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License  for
// more details.
//
// You  should  have received a copy of the  GNU General Public License  along
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// == module ===================================================================
//
// SDOUBLES.CPP
// The class defines an array of ordered numeric elements with null value.
//
// Author: Marco Vettigli
//
// This class is used as a data container for storing numeric values. Despite
// its array form, the class is defined to fit the role of numeric column in
// STable object. Apart from data, the only property of the array is its
// name which is not unique amoung other SDoubles objects.
//
// Because of its numeric nature, few more functions are implemented compared
// to SStrings, such as max(), min() and range(), because directly related to
// array status. All other statistical functions are to be implemented
// separately as static member of forecoming accessor class.
//
// == implementation ===========================================================
//
// Data are stored through a QVector of doubles and access to its properties
// (such as values and element numbers) is provided through a public interface.
// This includes assign() and get() for data values, insert(), append() and
// delete() for operation on data structure.
// QVector 'data' must be kept private and not directly accessable by user.
// Null value is implemented losing double minumum value (not in absolute
// term) and setting it as null. By default, convertion from QString values
// was implemented only in setter functions.
//
// =============================================================================

// == include directives =======================================================

#include <limits>
#include "sdoubles.h"

// == constructors and destructors =============================================

StatS::SDoubles::SDoubles(const QString name)
{
    data = new QVector<double>();
    this->_name = name;
}

// == getters ==================================================================

int StatS::SDoubles::size() const
{
    return data->size();
}

int StatS::SDoubles::count() const
{
    int counter = 0;
    for (int i = 0; i< data->size(); i++)
        if (!isNull(i)) counter++;
    return counter;
}

double StatS::SDoubles::get(int pos) const
{
    if (!isIndex(pos)) return null;
    return data->at(pos);
}

double StatS::SDoubles::min() const
{
    double min_temp = std::numeric_limits<double>::max();
    for (int i = 0;i < data->size(); i++)
        if (!isNull(i) && data->at(i)<min_temp) min_temp=data->at(i);
    return min_temp;
}

double StatS::SDoubles::max() const
{
    double max_temp = -std::numeric_limits<double>::max();
    for (int i = 0;i < data->size(); i++)
        if (!isNull(i) && data->at(i)>max_temp) max_temp=data->at(i);
    return max_temp;
}

double StatS::SDoubles::range() const
{
    return max() - min();
}

QString StatS::SDoubles::name() const
{
    return _name;
}

// == setters ==================================================================

bool StatS::SDoubles::assign(double val,
                             int pos)
{
    if (!isIndex(pos)) return false;
    data->replace(pos, val);
    return true;
}

bool StatS::SDoubles::assign(QString val,
                             int pos)
{
    if (!isIndex(pos)) return false;
    bool is_castable = false;
    double double_cast = val.toDouble(&is_castable);
    if (is_castable) data->replace(pos, double_cast);
    else data->replace(pos, null);
    return true;
}

bool StatS::SDoubles::setName(const QString name)
{
    if (name.isEmpty()) return false;
    else this->_name = name;
    return true;
}

// == inspectors ===============================================================

bool StatS::SDoubles::isNull(int pos) const
{
    return (isIndex(pos) && (data->at(pos) == null));
}

bool StatS::SDoubles::isIndex(int pos) const
{
    if (pos >= 0 && pos < data->size()) return true;
    else return false;
}

// == accessor function definitions ============================================

bool StatS::SDoubles::append(double val,
                             int num)
{
    for (int i=0; i < num; i++)
        data->append(val);
    return true;
}

bool StatS::SDoubles::append(QString val,
                             int num)
{
    bool is_castable = false;
    double double_cast = val.toDouble(&is_castable);
    if (is_castable) return this->append(double_cast, num);
    else return this->append(null, num);
}

bool StatS::SDoubles::insert(double val,
                             int pos,
                             int num)
{
    if (isIndex(pos) || pos == data->size()) {
        data->insert(pos, num, val);
        return true;
    } else return false;
}

bool StatS::SDoubles::insert(QString val,
                             int pos,
                             int num)
{
    if (!isIndex(pos)) return false;
    bool is_castable = false;
    double double_cast = val.toDouble(&is_castable);
    if (is_castable) return this->insert(double_cast, pos, num);
    else return this->insert(null, pos, num);
}

bool StatS::SDoubles::remove(int pos,
                             int num)
{
    // check if remove is possible
    if (!isIndex(pos)) return false;
    if (pos + num > data->size()) return false;
    // remove data and return
    data->remove(pos, num);
    return true;
}

QString StatS::SDoubles::debug() const
{
    QString out;
    // create array header
    out = QString("%1 [size=%2, count=%3] ")
            .arg(name())
            .arg(size())
            .arg(count());
    // append array content
    for (int i = 0; i < size(); i++)
        out.append(QString("(%1)_%2, ")
                   .arg(i)
                   .arg(get(i)));
    // append footer and return
    out.append("[END]");
    return out;
}

bool StatS::SDoubles::clear()
{
    data->clear();
    return true;
}


// == static variables =========================================================

const double StatS::SDoubles::null = std::numeric_limits<double>::min();

// == static funtion definitions ===============================================

// == private function definitions =============================================

